home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / stevi69s.zip / SETENV.C < prev    next >
Text File  |  1990-04-23  |  14KB  |  527 lines

  1. /*****************************************************************************
  2.  * A program for adding or changing environment variable values for MSDOS.
  3.  * The "set" command provided by command.com is very limited.  It fails to
  4.  * provide the ability to use quotation marks and escape characters and
  5.  * octal/hex constants in the value definition.  Setenv provides these
  6.  * abilities.
  7.  *
  8.  * Usage notes:
  9.  *
  10.  *    setenv <symbol> = <value>
  11.  *
  12.  *    <symbol> ::= legal MSDOS environment symbol.  Lower case converted
  13.  *             to uppercase.
  14.  *
  15.  *    <value>  ::= environment symbol value in one of three forms:
  16.  *
  17.  *             * No quotation marks.  The value is the literal string
  18.  *               of characters starting IMMEDIATELY after the equal
  19.  *               sign and extending to the end-of-line.
  20.  *
  21.  *             * Single quotation marks (').  The value is the literal
  22.  *               string enclosed in quotation marks.
  23.  *
  24.  *             * Double quotation marks (").  The value is the string
  25.  *               enclosed in double quotation marks.  Backslash escape
  26.  *               constructions are processed -- this includes the usual
  27.  *               C language constructions such as \n for newline and
  28.  *               \r for carriage return plus octal and hexadecimal
  29.  *               constants (\ddd & \0xdd, respectively).
  30.  *****************************************************************************/
  31.  
  32. /*****************************************************************************
  33.  * Based on a program by Alan J Myrvold (ajmyrvold@violet.waterloo.edu)
  34.  *
  35.  * WARNING WARNING WARNING - virtually no error checking is done !!
  36.  *                           use at own risk !!
  37.  *
  38.  * This program by Larry A. Shurr (las@cbema.ATT.COM)
  39.  *
  40.  * I added checking for env seg overrun, so now it's a little more robust.
  41.  *****************************************************************************/
  42.  
  43. /*****************************************************************************
  44.  *
  45.  * Notes by Alan J Myrgold:
  46.  *
  47.  * Technical information : A program's PSP contains a pointer at
  48.  * offset 44 (decimal) to a COPY of the parent's environment.
  49.  * The environment is a set of strings of the form NAME=value,
  50.  * each terminated by a NULL byte.
  51.  * An additional NULL byte marks the end of the environment.
  52.  * The environment area is ALWAYS paragraph aligned
  53.  * i.e. on a 16 byte boundary.
  54.  *
  55.  * Searching backwards from the PSP, I consistently find
  56.  * two copies of the envronment area.
  57.  *
  58.  * The program : finds the two areas
  59.  *               reads one into memory
  60.  *               udpates the specified environment variable
  61.  *               writes updated environment to parent environment
  62.  v1.1 Toad Hall Tweak, 20 Apr 90
  63.  *****************************************************************************/
  64.  
  65. #include <stdio.h>
  66. #include <stdlib.h>
  67. #include <string.h>
  68. #include <time.h>
  69. #include <process.h>
  70. #include <conio.h>
  71. #include <dos.h>
  72.  
  73. #define FALSE 0
  74. #define TRUE  1
  75.  
  76. struct mcb {                /* MSDOS Memory Control Block */
  77.   unsigned char tag4D;        /* Tag field must = 0x4D */
  78.   unsigned int  next;        /* Segment base for next block */
  79.   unsigned int  size;        /* Memory block size in paragraphs */
  80. };
  81.  
  82.  
  83. unsigned env_size = 0;        /* Maintain size of environment */
  84. /***************************************************************************/
  85. int env_size_bytes(unsigned env_seg)
  86. /* Determine the length of the environment area in bytes */
  87. {
  88.     int n;
  89.  
  90.     n = 0;
  91.     while (peekb(env_seg,n) != 0) {
  92.         while (peekb(env_seg,n) != 0) n++;
  93.         n++;
  94.     }
  95.     return(n);
  96. }
  97. /***************************************************************************/
  98. int env_size_strings(unsigned env_seg)
  99. /* Determine how many strings are in the environment area */
  100. {
  101.     int k,n;
  102.  
  103.     k = n = 0;
  104.     while (peekb(env_seg,n) != 0) {
  105.         k++;
  106.         while (peekb(env_seg,n) != 0) n++;
  107.         n++;
  108.     }
  109.     return(k);
  110. }
  111. /***************************************************************************/
  112. int peek_cmp(unsigned seg1,unsigned seg2,int nbytes)
  113. /* A trivial compare routine for segement aligned data items */
  114. {
  115.    int i;
  116.  
  117.    for (i = 0; (i < nbytes) && (peekb(seg1,i) == peekb(seg2,i)); i++);
  118.    return(i == nbytes);
  119. }
  120. /***************************************************************************/
  121. void find_env(unsigned seg_ray[2])
  122. {
  123.     unsigned psp_seg,copy_of_seg,env_seg;
  124.     int k,n;
  125.  
  126. /* Find first copy of environment */
  127.     psp_seg = _psp;
  128.     copy_of_seg = peek(psp_seg,44);
  129.  
  130. /* Set return value to non-garabage */
  131.     seg_ray[0] = seg_ray[1] = copy_of_seg;
  132.  
  133. /* Search back to find 2 copies of environment */
  134.     env_size = n = env_size_bytes(copy_of_seg);
  135.     env_seg = copy_of_seg - 1;
  136.     for (k = 0; (env_seg != 0) && (k < 2); k++) {
  137.         while ((env_seg != 0)
  138.         && (peek_cmp(copy_of_seg,env_seg,n) == 0)) {
  139.            env_seg--;
  140.         }
  141.         if (env_seg != 0) {
  142.             seg_ray[k] = env_seg;
  143.             env_seg--;
  144.         }
  145.     }
  146.  
  147. /* If not found, display error message and abort */
  148.     if (k != 2) {
  149.         fprintf(stderr,"Two copies of the environment were not found\n");
  150.         exit(-1);
  151.     }
  152. }
  153. /***************************************************************************/
  154. void read_env(unsigned env_seg,int *k,char ***s,char ***t)
  155. /* Read environment into a malloc'd array of malloc'd strings */
  156. {
  157.     int i,j,n;
  158.  
  159.     env_size = env_size_bytes(env_seg);
  160.  
  161.     *k = env_size_strings(env_seg);
  162.     *s = (char **) malloc((*k)*sizeof(char *));
  163.     *t = (char **) malloc((*k)*sizeof(char *));
  164.  
  165.     n = 0;
  166.     for (i = 0; i < *k; i++) {
  167.         for (j = 0; peekb(env_seg,n+j) != '='; j++);
  168.         (*s)[i] = (char *) malloc(j+1);
  169.         for (j = 0; peekb(env_seg,n+j) != '='; j++)
  170.             ((*s)[i])[j] = peekb(env_seg,n+j);
  171.         ((*s)[i])[j] = 0;
  172.         n += j + 1;
  173.         for (j = 0; peekb(env_seg,n+j) != 0; j++);
  174.         (*t)[i] = (char *) malloc(j+1);
  175.         for (j = 0; peekb(env_seg,n+j) != 0; j++)
  176.             ((*t)[i])[j] = peekb(env_seg,n+j);
  177.         ((*t)[i])[j] = 0;
  178.         n += j + 1;
  179.     }
  180. }
  181. /***************************************************************************/
  182. void write_env(unsigned env_seg, int k, char **s, char **t)
  183. /* Write the environment back out to memory */
  184. {
  185.     int i,j,n;
  186.  
  187.     struct mcb far *tmcb = (struct mcb far *)((long)(env_seg-1) << 16);
  188.  
  189.     if (tmcb->tag4D == 0x4D) {
  190.         unsigned env_seg_siz = tmcb->size << 4;
  191.         if (env_size < env_seg_siz) {
  192.             for (n = i = 0; i < k; i++) {
  193.                 char *si = s[i];
  194.                 char *ti = t[i];
  195.                 for (j = 0; si[j] != 0; j++) pokeb(env_seg,n++,si[j]);
  196.                 pokeb(env_seg,n++,'=');
  197.                 for (j = 0; ti[j] != 0; j++) pokeb(env_seg,n++,ti[j]);
  198.                 pokeb(env_seg,n++,0);
  199.             }
  200.             pokeb(env_seg,n,0);
  201.         } else {
  202.             fprintf(stderr,"Insufficient space in environment\n");
  203.             exit(-1);
  204.         }
  205.     } else {
  206.         fprintf(stderr,"Environment memory control block trashed\n");
  207.         exit(-1);
  208.     }
  209. }
  210. /***************************************************************************/
  211. char *get_env_var(int k,char **s,char **t,char *var)
  212. /* Return the value of the environment variable or NULL if not found */
  213. {
  214.     char *val;
  215.     int i;
  216.  
  217.     val = NULL;
  218.     for (i = 0; i < k; i++) if (stricmp(s[i],var) == 0) val = t[i];
  219.  
  220.     return(val);
  221. }
  222.  
  223. /***************************************************************************/
  224. void set_env_var(int *k,char ***s,char ***t,char *var,char *val)
  225. /* Set a new or existing environment variable to a new value */
  226. {
  227.     int i,done;
  228.  
  229.     done = 0;
  230.     for (i = 0; i < *k; i++) {
  231.         if (stricmp((*s)[i],var) == 0) {
  232.             /* Existing variable */
  233.             done = 1;
  234.             env_size -= strlen((*t)[i]);
  235.             free((*t)[i]);
  236.             (*t)[i] = (char *) malloc(1+strlen(val));
  237.             strcpy((*t)[i],val);
  238.             env_size += strlen((*t)[i]);
  239.         }
  240.     }
  241.  
  242.     if (!done) {
  243.         /* New environment variable */
  244.         (*k)++;
  245.         *s = realloc(*s,(*k)*sizeof(char *));
  246.         *t = realloc(*t,(*k)*sizeof(char *));
  247.         (*s)[*k-1] = (char *) malloc(1+strlen(var));
  248.         strcpy((*s)[*k-1],var);
  249.         strupr((*s)[*k-1]);
  250.         (*t)[*k-1] = (char *) malloc(1+strlen(val));
  251.         strcpy((*t)[*k-1],val);
  252.     /* Length of name  + length of '=' + length of value + length of '\0' */
  253.         env_size += (strlen((*s)[*k-1]) + 1 + strlen((*t)[*k-1]) + 1);
  254.     }
  255. }
  256. /***************************************************************************/
  257. void show_env(int k,char **s,char **t)
  258. /* Display the array of environment strings */
  259. {
  260.     int i;
  261.     for (i = 0; i < k; i++) printf("%s=%s\n",s[i],t[i]);
  262. }
  263. /***************************************************************************/
  264. void get_cmdline(char *cmd)
  265. /* Read raw command line text into string buffer */
  266. {
  267.     char far *pcmd;
  268.  
  269.     int idx,odx;
  270.  
  271.     pcmd = (char far *)((long)_psp << 16) + 128L;
  272.  
  273.     for (idx = *pcmd++, odx = 0; idx > 0; idx--, odx++) {
  274.       cmd[odx] = *pcmd++;
  275.     }
  276.  
  277.     cmd[odx] = '\0';
  278. }
  279. /***************************************************************************/
  280. char_in(char ch, char *set)
  281. /* Determine if a character is in a set of characters */
  282. {
  283.     do {
  284.         if (ch == *set) return(TRUE);
  285.     } while ((int)*(++set));
  286.     return(FALSE);
  287. }
  288. /***************************************************************************/
  289. char get_num(char *cmd, int *pidx)
  290. /* Interpret octal or hexadecimal constant in string */
  291. {
  292.     int   accum  = 0;
  293.     char  ch;
  294.     int   f_scan = TRUE;
  295.     int   idx    = *pidx;
  296.     int   limit;
  297.     char *nch    = cmd+idx;
  298.     char *och    = nch+1;
  299.     int   radix;
  300.  
  301. #define HEXDIG "0123456789ABCDEFabcdef"
  302.  
  303.     if (*nch == '0' && char_in(*och,"xX") && char_in(*(och+1),HEXDIG)) {
  304.         radix = 16;
  305.         limit = 2;
  306.         och += 1;
  307.     } else {
  308.         radix = 8;
  309.         limit = 3;
  310.         och = nch;
  311.     }
  312.  
  313.     while (limit-- > 0 && f_scan) {
  314.  
  315.         f_scan = FALSE;
  316.  
  317.     while ((int)(*nch)) *nch++ = *och++;
  318.  
  319.     nch = cmd+idx;
  320.     och = nch+1;
  321.  
  322.     switch (ch = *nch) {
  323.         case '0' :
  324.         case '1' :
  325.         case '2' :
  326.         case '3' :
  327.         case '4' :
  328.         case '5' :
  329.         case '6' :
  330.         case '7' :
  331.         case '8' :
  332.         case '9' :
  333.             if (ch == 9 && radix == 8) break;
  334.             accum = accum * radix + (int)(ch - '0');
  335.             f_scan = TRUE;
  336.             break;
  337.         case 'A' :
  338.         case 'B' :
  339.         case 'C' :
  340.         case 'D' :
  341.         case 'E' :
  342.         case 'F' :
  343.             if (radix == 8) break;
  344.             accum = accum * radix + (int)(ch - 'A') + 10;
  345.             f_scan = TRUE;
  346.             break;
  347.         case 'a' :
  348.         case 'b' :
  349.         case 'c' :
  350.         case 'd' :
  351.         case 'e' :
  352.         case 'f' :
  353.             if (radix == 8) break;
  354.             accum = accum * radix + (int)(ch - 'a') + 10;
  355.             f_scan = TRUE;
  356.             break;
  357.       default  : break;
  358.     }
  359.     }
  360.  
  361.     *pidx = idx;
  362.     return(accum);
  363. }
  364. /***************************************************************************/
  365. void get_escape(char *cmd, int *pidx, char quote)    /* v1.1 */
  366. /* Interpret escape'd (i.e., '\' (backslash) character */
  367. {
  368.   int   idx = *pidx;
  369.  
  370.   if (quote == '"') {
  371.     char *nch = cmd+idx;
  372.     char *och = nch+1;
  373.     char *xch = nch;
  374.     while ((int)(*nch)) *nch++ = *och++;
  375.     switch (*xch) {
  376.       case 'a' : *xch = '\a'; break;
  377.       case 'b' : *xch = '\b'; break;
  378.       case 'c' : *xch = '\c'; break;
  379.       case 'd' : *xch = '\d'; break;
  380.       case 'e' : *xch = '\e'; break;
  381.       case 'f' : *xch = '\f'; break;
  382.       case 'g' : *xch = '\g'; break;
  383.       case 'h' : *xch = '\h'; break;
  384.       case 'i' : *xch = '\i'; break;
  385.       case 'j' : *xch = '\j'; break;
  386.       case 'k' : *xch = '\k'; break;
  387.       case 'l' : *xch = '\l'; break;
  388.       case 'm' : *xch = '\m'; break;
  389.       case 'n' : *xch = '\n'; break;
  390.       case 'o' : *xch = '\o'; break;
  391.       case 'p' : *xch = '\p'; break;
  392.       case 'q' : *xch = '\q'; break;
  393.       case 'r' : *xch = '\r'; break;
  394.       case 's' : *xch = '\s'; break;
  395.       case 't' : *xch = '\t'; break;
  396.       case 'u' : *xch = '\u'; break;
  397.       case 'v' : *xch = '\v'; break;
  398.       case 'w' : *xch = '\w'; break;
  399.       case 'x' : *xch = '\x'; break;
  400.       case 'y' : *xch = '\y'; break;
  401.       case 'z' : *xch = '\z'; break;
  402.       case '0' :
  403.       case '1' :
  404.       case '2' :
  405.         *xch = get_num(cmd, &idx);
  406.         break;
  407.     }
  408.   }
  409.  
  410.   *pidx = idx + 1;
  411. }
  412. /***************************************************************************/
  413. get_qvalue(char *cmd, int idx, char quote, char **value)
  414. /* Extract a quoted value part from command line */
  415. {
  416.     char ch;
  417.     int  f_esc;
  418.  
  419.     *value = cmd + (++idx);
  420.  
  421.     do {
  422.         while ((int)(ch = cmd[idx]) && ch != '\\' && ch != quote) idx++;
  423.         if(!(int)ch)
  424.             return(-1);
  425.         if (ch == '\\') {
  426.             f_esc = TRUE;
  427.             get_escape(cmd, &idx, quote);
  428.         } else f_esc = FALSE;
  429.     } while (f_esc);
  430.  
  431.     cmd[idx] = '\0';
  432.  
  433.     for (idx += 1; (int)(ch = cmd[idx]) && ch == ' '; idx++);
  434.  
  435.     if ((int)ch) return(-1);
  436.  
  437.     return(0);
  438. }
  439. /***************************************************************************/
  440. get_parm(char **name, char **value)
  441. /* Extract environment symbol name and value from command line */
  442. {
  443.     char ch;
  444.     int  idx;
  445.     int  sdx;
  446.  
  447.     static char cmd[128];
  448.  
  449.     get_cmdline(cmd);
  450.  
  451.     for (idx = 0; (int)(ch = cmd[idx]) && ch  == ' '; idx++);
  452.  
  453.     if (!(int)ch) return(1);
  454.  
  455.     *name = cmd + idx;
  456.  
  457.     for (; (int)(ch = cmd[idx]) && ch != '=' && ch != ' '; idx++);
  458.  
  459.     if (!(int)ch) return(-1);
  460.  
  461.     if (ch == ' ') {
  462.       cmd[idx] = '\0';
  463.       for (idx += 1; (int)(ch = cmd[idx]) && ch != '='; idx++);
  464.     } else cmd[idx] = '\0';
  465.  
  466.     if (!(int)ch) return(-1);
  467.  
  468.     for (sdx = (idx += 1); (int)(ch = cmd[idx]) && ch == ' '; idx++);
  469.  
  470.     /*if (!(int)ch) return(-1);*/
  471.  
  472.     switch (ch) {
  473.       case '"'  : return(get_qvalue(cmd, idx, '"', value));
  474.       case '\'' : return(get_qvalue(cmd, idx, '\'', value));
  475.       default   : *value = cmd + sdx; break;
  476.     }
  477.  
  478.     return(0);
  479. }
  480. /***************************************************************************/
  481. /* main(int argc,char **argv) v1.1 argc,argv never used */
  482. main()            /* v1.1 */
  483. {
  484.     unsigned env_seg[2];
  485.     char **s,**t;
  486.     int k;
  487. /* v1.1 never used
  488.     long now;
  489.     struct tm *local;
  490. */
  491.  
  492.     static char *name = NULL, *value = NULL;
  493.  
  494.     switch (get_parm(&name,&value)) {
  495.  
  496.     case -1:
  497.  
  498.     fprintf(stderr,"Invalid symbol definition syntax\n");
  499.     exit(-1);
  500.  
  501.     case 0:
  502.  
  503.         /* Find and read environment */
  504.  
  505.         find_env(env_seg);
  506.         read_env(env_seg[1],&k,&s,&t);
  507.  
  508.     /* Set the variable <name> to <value> */
  509.  
  510.     set_env_var(&k,&s,&t,name,value);
  511.  
  512.     /* Update caller's environment */
  513.  
  514.     write_env(env_seg[0],k,s,t);
  515.  
  516.     break;
  517.  
  518.     case 1:
  519.  
  520.     fprintf(stderr,"Usage: setenv <symbol name> = <value>\n");
  521.     break;
  522.     }
  523.  
  524.     return(0);
  525. }
  526. /***************************************************************************/
  527.